<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        /* 扩展运算符 - rest参数的逆运算 */

        console.log(...[1, 2, 3]);
        let add = (x, y) => x + y;
        const args = [0, 1];
        const args2 = [2, 3, 4];
        console.log(add(...args));
        function f(v, w, x, y, z) { console.log([v, w, x, y, z]) }
        f(-1, ...args, 2, ...[3]);

        // 可以取代apply方法 eg.Math.max
        console.log(Math.max.apply(null, args));
        console.log(Math.max(...args));

        // 通过push将数组添加到另一个数组的尾部
        args.push(args2);
        console.log(args); // [0, 1, [2, 3, 4]]
        Array.prototype.push.apply(args, args2);
        console.log(args); // [0, 1, 2, 3, 4]
        args.push(...args2);
        console.log(args); // [0, 1, 2, 3, 4]

        // 复制数组
        const a1 = [1, 2, 3];
        let a2 = a1;
        a2[0] = -1;
        console.log(a1);
        // ↑ 复制指针, 修改a2会导致同时变化
        const a3 = [...a1]; // const a3 = a1.contat();
        a3[0] = 0;
        console.log(a1);
        // ↑ 复制数组, a2是a1的克隆, 互不影响

        // 合并数组
        let a4 = a1.concat(a2, a3); // es5
        let a5 = [...a1, ...a2, ...a3]; //es6
        console.log(a4, a5, a4[0] === a5[0]);
        // ↑ 两种方法都是对原数组成员的引用, 是浅拷贝, 修改引用指向的值会同步反应到新数组

        // 与解构结合, 赋值时只能放在参数最后一位
        let list = ['aa', 1, 2, 3, 4];
        // const a = list[0], rest = list.slice(1); // es5
        [a, ...rest] = list; // es6
        console.log(a, rest);
        // 其他例子 ↓
        // const [first, ...rest] = [];  // first = undefined, rest = []
        // const [first, ...rest] = ["foo"];  // first = "foo", rest = []

        // 把字符串转换为数组, 能正确识别四个字节的Unicode字符
        // 涉及到操作四个字节的 Unicode字符的函数用...改写
        console.log('x\uD83D\uDE80y'.length); // 4
        console.log([...'x\uD83D\uDE80y'].length); // 3

        // Array.from() - 把类数组对象转换成数组
        let arrayLike = {
            '0': 'a',
            '1': 'b',
            '2': 'c',
            length: 3
        };
        let arr1 = [].slice.call(arrayLike); // es5 ['a', 'b', 'c']
        let arr2 = Array.from(arrayLike); // es6 ['a', 'b', 'c']
        // 把Map和Set变为数组
        const sett = new Set([1, 2, 3, 4, 5]);
        const mapp = new Map([['aa', 1], ['bb', 2]]);
        console.log('set & map change to Array', Array.from(sett), Array.from(mapp)); 
        // 第二个参数进行对数组元素的操作,返回操作后的值
        let arr3 = Array.from([0, 1, 2], (x) => x * x);
        console.log(arr1, arr2, arr3);
        // 应用 - 取出DOM节点的文本内容
        // let spans = document.querySelectorAll('span.name');
        // let names2 = Array.from(spans, s => s.textContent);
        // 应用 - 筛选数组
        console.log(Array.from([1, , 2, , 3], (n) => n || '不存在')); // [1, 0, 2, 0, 3]

        // Array.of() - 替代Array(), new Array() 
        console.log(Array.of(1, 2, 3, 4));
        // 手写
        function ArrayOf() {
            return Array.prototype.slice.call(arguments);
        }
        console.log(ArrayOf(1, 2, 3, 4));

        // 复制方法 - fill(), 填充方法 - copyWithin()
        let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
        // 从1开始插入6, 到3结束 - fill
        console.log(arr.fill(6, 1, 3));
        // 从1开始插入6, 7, 8 !!1会被替换 - copyWithin()
        console.log(arr.copyWithin(1, 6, 9)); // [0, 6, 7, 8, 4, 5, 6, 7, 8, 9]

        // 断言函数, 接收三个参数 - element/value, index, array
        // find() findIndex() 
        const people = [{ name: "18 years old", age: 18 }, { name: "19 years old", age: 19 }];
        console.log(people.find((element, index, array) => element.age > 18));
        console.log(people.findIndex((element, index, array) => element.age > 18));
        console.log([1, 2, 3, 4, 5].find((value, index, array) => value < 4));

        // 迭代器方法 keys() values() entries()
        const numbers = ['one', 'two', 'three', 'four', 'five'];
        // keys() 元素索引的迭代器, 以数组形式取出
        console.log(Array.from(numbers.keys()));
        // values() 元素的迭代器, 用for..of循环遍历取出
        for (let elements of numbers.values()) {
            console.log(elements);
        }
        // entries() 索引/元素键值对的迭代器, 用迭代器对象next方法取出
        console.log(numbers.entries().next().value);
        // for..of循环遍历取出, 结合解构拆分键值对(数组)
        for (let [index, element] of numbers.entries()) {
            console.log(index, element);
        }

        // 查找数组元素 includes(), 返回布尔值 indexOf()/lastIndexOf(), 返回元素位置
        console.log(numbers.includes('one'), numbers.indexOf('one'));
        console.log(numbers.includes('one', 1), numbers.indexOf('one', 1));
        console.log([NaN].includes(NaN), [NaN].indexOf(NaN)); // indexOf导致误判

        // 数组扁平化 flat() flatMap()
        const arrFlat1 = [1, [2, 3, 4, [5, [6]]], 7];
        const arrFlat2 = [1, 2, 3, [4], 5];
        console.log(arrFlat1.flat(Infinity));
        const arrFlatMap = arrFlat2.flatMap((x) => x * 2);
        console.log(arrFlatMap);
    </script>
</body>

</html>